package com.roger.match.library.util;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.utils.Color;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Point;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

import java.util.ArrayList;


/**
 * Description:MatchBaseTextView
 * User: Lj
 * Date: 2014-12-03
 * Time: 07:48
 * FIXME
 */
public class MatchView extends Component implements Component.DrawTask, Component.EstimateSizeListener {
    public ArrayList<MatchItem> mItemList = new ArrayList<MatchItem>();

    private int mLineWidth;
    private float mScale = 1f;
    private int mDropHeight;
    private float internalAnimationFactor = 0.7f;
    private int horizontalRandomness;
    private float mProgress = 0;
    private int mDrawZoneWidth = 0;
    private int mDrawZoneHeight = 0;
    private int mOffsetX = 0;
    private int mOffsetY = 0;
    private float mBarDarkAlpha = 0.4f;
    private float mFromAlpha = 1.0f;
    private float mToAlpha = 0.4f;
    private int mLoadingAniDuration = 1000;
    private int mLoadingAniSegDuration = 1000;
    private int mLoadingAniItemDuration = 400;
    private boolean mIsInLoading = false;
    private AniController mAniController = new AniController();
    private int mTextColor = Color.WHITE.getValue();
    private MyEventHandler mHandler;
    private float progress = 0;
    private float mInTime = 0.8f;
    private float mOutTime = 0.8f;
    private boolean isBeginLight = true;
    private int mPaddingTop = 15;
    private float mTextSize = 25f;

    private int STATE = 0;

    private MatchInListener mMatchInListener;
    private MatchOutListener mMatchOutListener;
    private MatchAnimateRoundListener mMatchAnimateRoundListener;

    public void setMatchInListener(MatchInListener mMatchInListener) {
        this.mMatchInListener = mMatchInListener;
    }

    public void setMatchOutListener(MatchOutListener mMatchOutListener) {
        this.mMatchOutListener = mMatchOutListener;
    }

    public void setMatchAnimateRoundListener(MatchAnimateRoundListener mMatchAnimateRoundListener) {
        this.mMatchAnimateRoundListener = mMatchAnimateRoundListener;
    }

    /**
     * 初始化
     *
     * @param context context
     */
    public MatchView(Context context) {
        super(context);
        initView();
    }

    /**
     * 初始化
     *
     * @param context context
     * @param attrs   attrs
     */
    public MatchView(Context context, AttrSet attrs) {
        super(context, attrs);
        initView();
    }

    /**
     * 初始化
     *
     * @param context      context
     * @param attrs        attrs
     * @param defStyleAttr defStyleAttr
     */
    public MatchView(Context context, AttrSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    class MyEventHandler extends EventHandler {
        private MyEventHandler(EventRunner runner) {
            super(runner);
        }

        @Override
        public void distributeEvent(InnerEvent event) {
            super.distributeEvent(event);
            if (event == null) {
                return;
            }
            if (STATE == 1) {
                if (progress < 100) {
                    progress++;
                    setProgress((progress * 1f / (100)));
                    mHandler.sendEvent(0, (long) (mInTime * 10));
                } else {
                    STATE = 2;
                    if (mMatchInListener != null) {
                        mMatchInListener.onFinish();
                        mHandler.removeAllEvent();
                    }
                }
            } else if (STATE == 2) {
                if (mIsInLoading) {
                    lightFinish();
                }
                if (progress > 0) {
                    progress--;
                    setProgress((progress * 1f / (100)));
                    mHandler.sendEvent(0, (long) (mOutTime * 10));
                } else {
                    progress = 0;
                    if (mMatchOutListener != null) {
                        mMatchOutListener.onFinish();
                        mHandler.removeAllEvent();
                    }
                    STATE = 1;
                }
            }
        }

    }

    private void initView() {
        setEstimateSizeListener(this);
        addDrawTask(this);
        Utils.init(getContext());
        mLineWidth = Utils.dp2px(1);
        mDropHeight = Utils.dp2px(40);
        horizontalRandomness = Utils.SCREEN_WIDTH_PIXELS / 2;

        setPadding(0, Utils.dp2px(mPaddingTop), 0, Utils.dp2px(mPaddingTop));
        mItemList.clear();
        mHandler = new MyEventHandler(EventRunner.getMainEventRunner());
    }

    public void setInTime(float mTime) {
        mInTime = mTime;
    }

    public void setOutTime(float mTime) {
        mOutTime = mTime;
    }

    public void setLight(boolean isLight) {
        isBeginLight = isLight;
    }

    /**
     * 设置填充顶部
     *
     * @param dp dp
     */
    public void setPaddingTop(int dp) {
        mPaddingTop = dp;
    }

    /**
     * 设置文字大小
     *
     * @param mTextSize mTextSize
     */
    public void setTextSize(float mTextSize) {
        this.mTextSize = mTextSize;
    }

    protected void show() {
        if (mItemList.size() == 0) {
            return;
        }
        STATE = 1;
        mHandler.sendEvent(0);
        mHandler.getEventRunner().stop();
        if (mMatchInListener != null) {
            mMatchInListener.onBegin();
        }
    }

    public void hide() {
        if (mMatchOutListener != null) {
            mMatchOutListener.onBegin();
        }
        mHandler.sendEvent(0);
        mHandler.getEventRunner().stop();
    }

    public void setProgress(float progress) {
        if (mMatchInListener != null && STATE == 1) {
            mMatchInListener.onProgressUpdate(progress);
        } else if (mMatchOutListener != null && STATE == 2) {
            mMatchOutListener.onProgressUpdate(progress);
        }

        if (progress == 1) {
            if (isBeginLight) {
                beginLight();
            }
        } else if (mIsInLoading) {
            lightFinish();
        }
        mProgress = progress;

        invalidate();
    }

    public int getLoadingAniDuration() {
        return mLoadingAniDuration;
    }

    /**
     * 设置加载动画持续时间
     *
     * @param duration duration
     */
    public void setLoadingAniDuration(int duration) {
        mLoadingAniDuration = duration;
        mLoadingAniSegDuration = duration;
    }

    public MatchView setLineWidth(int width) {
        mLineWidth = width;
        for (int i = 0; i < mItemList.size(); i++) {
            mItemList.get(i).setLineWidth(width);
        }
        return this;
    }

    /**
     * 设置文字颜色
     *
     * @param color color
     * @return MatchView
     */
    public MatchView setTextColor(int color) {
        mTextColor = color;
        for (int i = 0; i < mItemList.size(); i++) {
            mItemList.get(i).setColor(color);
        }
        return this;
    }

    public MatchView setDropHeight(int height) {
        mDropHeight = height;
        return this;
    }

    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        int height = getTopOffset() + mDrawZoneHeight + getBottomOffset();
        heightMeasureSpec = EstimateSpec.getSizeWithMode(height, EstimateSpec.PRECISE);
        setEstimatedSize(widthMeasureSpec, heightMeasureSpec);

        mOffsetX = (getEstimatedWidth() - mDrawZoneWidth) / 2;
        mOffsetY = getTopOffset();
        mDropHeight = getTopOffset();
        return false;
    }

    /**
     * 获取屏幕宽度
     *
     * @param context 上下文
     * @return 屏幕宽度
     */
    public static int getDisplayWidthInPx(Context context) {
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        Point point = new Point();
        display.getSize(point);
        return (int) point.getPointX();
    }

    private int getTopOffset() {
        return getPaddingTop() + Utils.dp2px(10);
    }

    private int getBottomOffset() {
        return getPaddingBottom() + Utils.dp2px(10);
    }

    /**
     * 初始化字符串
     *
     * @param str str
     */
    public void initWithString(String str) {
        initWithString(str, mTextSize);
    }

    public void initWithString(String str, float fontSize) {
        ArrayList<float[]> pointList = MatchPath.getPath(str, fontSize * 0.01f, 14);
        initWithPointList(pointList);
    }

    public float getmScale() {
        return mScale;
    }

    /**
     * 设置比例
     *
     * @param scale scale
     */
    public void setScale(float scale) {
        mScale = scale;
    }

    public void initWithPointList(ArrayList<float[]> pointList) {
        float drawWidth = 0;
        float drawHeight = 0;
        boolean shouldLayout = mItemList.size() > 0;
        mItemList.clear();
        for (int i = 0; i < pointList.size(); i++) {
            float[] line = pointList.get(i);
            Point startPoint = new Point(Utils.dp2px(line[0]) * mScale, Utils.dp2px(line[1]) * mScale);
            Point endPoint = new Point(Utils.dp2px(line[2]) * mScale, Utils.dp2px(line[3]) * mScale);

            drawWidth = Math.max(drawWidth, startPoint.getPointX());
            drawWidth = Math.max(drawWidth, endPoint.getPointX());

            drawHeight = Math.max(drawHeight, startPoint.getPointY());
            drawHeight = Math.max(drawHeight, endPoint.getPointY());

            MatchItem item = new MatchItem(i, startPoint, endPoint, mTextColor, mLineWidth);
            item.resetPosition(horizontalRandomness);
            mItemList.add(item);
        }
        mDrawZoneWidth = (int) Math.ceil(drawWidth);
        mDrawZoneHeight = (int) Math.ceil(drawHeight);
        if (shouldLayout) {
            postLayout();
        } else {
            pointList.clear();
        }
    }

    public void beginLight() {
        mIsInLoading = true;
        mAniController.start();
        invalidate();
    }

    public void lightFinish() {
        mIsInLoading = false;
        mAniController.stop();
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        float progress = mProgress;
        int c1 = canvas.save();
        int len = mItemList.size();
        for (int i = 0; i < len; i++) {
            canvas.save();
            MatchItem loadingViewItem = mItemList.get(i);
            float offsetX = mOffsetX + loadingViewItem.midPoint.getPointX();
            float offsetY = mOffsetY + loadingViewItem.midPoint.getPointY();

            if (mIsInLoading) {
                loadingViewItem.setCurveTransformation();
                loadingViewItem.start();
                canvas.translate(offsetX, offsetY);
            } else {

                if (progress == 0) {
                    loadingViewItem.resetPosition(horizontalRandomness);
                    continue;
                }

                float startPadding = (1 - internalAnimationFactor) * i / len;
                float endPadding = 1 - internalAnimationFactor - startPadding;

                // done
                if (progress == 1 || progress >= 1 - endPadding) {
                    canvas.translate(offsetX, offsetY);
                    loadingViewItem.setAlpha(mBarDarkAlpha);
                } else {
                    float realProgress;
                    if (progress <= startPadding) {
                        realProgress = 0;
                    } else {
                        realProgress = Math.min(1, (progress - startPadding) / internalAnimationFactor);
                    }
                    offsetX += loadingViewItem.translationX * (1 - realProgress);
                    offsetY += -mDropHeight * (1 - realProgress);
                    Matrix matrix = new Matrix();
                    matrix.postRotate(360 * realProgress);
                    matrix.postScale(realProgress, realProgress);
                    matrix.postTranslate(offsetX, offsetY);
                    loadingViewItem.setAlpha(mBarDarkAlpha * realProgress);
                    canvas.concat(matrix);
                }
            }
            loadingViewItem.draw(canvas);
            canvas.restore();
        }
        if (mIsInLoading) {
            invalidate();
        }
        canvas.restoreToCount(c1);
    }

    private class AniController implements Runnable {

        private int mTick = 0;
        private int mCountPerSeg = 0;
        private int mSegCount = 0;
        private int mInterval = 0;
        private boolean mRunning = true;

        private void start() {
            mRunning = true;
            mTick = 0;

            mInterval = mLoadingAniDuration / mItemList.size();
            mCountPerSeg = mLoadingAniSegDuration / mInterval;
            mSegCount = mItemList.size() / mCountPerSeg + 1;
            run();
        }

        @Override
        public void run() {
            int pos = mTick % mCountPerSeg;
            for (int i = 0; i < mSegCount; i++) {

                int index = i * mCountPerSeg + pos;
                if (index > mTick) {
                    continue;
                }

                index = index % mItemList.size();
                MatchItem item = mItemList.get(index);
                item.setDuration(mLoadingAniItemDuration);
                item.start(mFromAlpha, mToAlpha);
                if (index == mItemList.size() - 1) {
                    if (mMatchAnimateRoundListener != null) {
                        mMatchAnimateRoundListener.onAnimateRoundFinish();
                    }
                }
            }
            mTick++;
            if (mRunning) {

            }
        }

        private void stop() {
            mRunning = false;
        }
    }

    public interface MatchInListener {
        void onBegin();

        void onProgressUpdate(float progress);

        void onFinish();
    }

    public interface MatchOutListener {
        void onBegin();

        void onProgressUpdate(float progress);

        void onFinish();
    }

    public interface MatchAnimateRoundListener {
        void onAnimateRoundFinish();
    }
}